package org.nci.platform.workflow.config;

import cn.hutool.core.util.PageUtil;
import cn.hutool.extra.spring.EnableSpringUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.flowable.engine.*;
import org.nci.platform.workflow.expand.system.service.WorkflowCategoryService;
import org.nci.platform.workflow.expand.system.service.WorkflowDeptService;
import org.nci.platform.workflow.expand.system.service.WorkflowRoleService;
import org.nci.platform.workflow.expand.system.service.WorkflowUserService;
import org.nci.platform.workflow.expand.system.service.impl.DemoCategoryImpl;
import org.nci.platform.workflow.expand.system.service.impl.DemoDeptImpl;
import org.nci.platform.workflow.expand.system.service.impl.DemoRoleImpl;
import org.nci.platform.workflow.expand.system.service.impl.DemoUserImpl;
import org.nci.platform.workflow.mapper.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Set;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-03 17:21
 */
@ComponentScan("org.nci.platform.workflow")
@Configuration
@EnableSpringUtil
@EnableConfigurationProperties({FlowableProperties.class, FlowableDatasource.class})
public class FlowableAutoConfig {
    @PostConstruct
    public void initConfig() {
        PageUtil.setOneAsFirstPageNo();
    }

    @Bean("platformWorkflowDatasource")
    public DataSource platformWorkflowDatasource(FlowableDatasource flowableDatasource) {
        return DataSourceBuilder.create()
                .url(flowableDatasource.getUrl())
                .type(flowableDatasource.getType())
                .driverClassName(flowableDatasource.getDriverClassName())
                .username(flowableDatasource.getUsername())
                .password(flowableDatasource.getPassword())
                .build();
    }

    @Bean
    @ConditionalOnMissingBean
    public ProcessEngine processEngine(FlowableProperties flowableProperties, @Qualifier("platformWorkflowDatasource") DataSource platformWorkflowDatasource,
                                       ApplicationContext applicationContext) {
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration
                .createStandaloneProcessEngineConfiguration();
        Set<Class<?>> customMappers = flowableProperties.getCustomMappers();
        customMappers.add(FlowDeployMapper.class);
        customMappers.add(SysDeployFormMapper.class);
        customMappers.add(SysExpressionMapper.class);
        customMappers.add(SysFormMapper.class);
        customMappers.add(SysListenerMapper.class);
        configuration.setCustomMybatisMappers(customMappers);
        Set<String> customXMLs = flowableProperties.getCustomXMLs();
        customXMLs.add("org/nci/platform/workflow/mapper/xml/FlowDeployMapper.xml");
        customXMLs.add("org/nci/platform/workflow/mapper/xml/SysDeployFormMapper.xml");
        customXMLs.add("org/nci/platform/workflow/mapper/xml/SysExpressionMapper.xml");
        customXMLs.add("org/nci/platform/workflow/mapper/xml/SysFormMapper.xml");
        customXMLs.add("org/nci/platform/workflow/mapper/xml/SysListenerMapper.xml");
        configuration.setCustomMybatisXMLMappers(customXMLs);
        configuration.setClassLoader(applicationContext.getClassLoader());
        // 数据源配置
        configuration.setDataSource(platformWorkflowDatasource);

        // 数据库策略
        configuration.setDatabaseSchemaUpdate(flowableProperties.getDatabaseSchemaUpdate());

        // 连接池配置
        configuration.setJdbcMaxActiveConnections(10);
        configuration.setJdbcMaxIdleConnections(5);

        // 其他配置
        configuration.setAsyncExecutorActivate(flowableProperties.getAsyncExecutorActivate());
        configuration.setHistoryLevel(flowableProperties.getHistoryLevel());

        return configuration.buildProcessEngine();
    }

    private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    private static final String DATE_PATTERN = "yyyy-MM-dd";

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);

    @Bean
    @ConditionalOnMissingBean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // Long 类型序列化为 String
            builder.serializerByType(Long.class, ToStringSerializer.instance);
            builder.serializerByType(Long.TYPE, ToStringSerializer.instance);

            // java.util.Date -> yyyy-MM-dd HH:mm:ss
            builder.serializerByType(Date.class, new JsonSerializer<Date>() {
                @Override
                public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeString(value != null ? new SimpleDateFormat(DATE_TIME_PATTERN).format(value) : null);
                }
            });

            // java.sql.Date -> yyyy-MM-dd
            builder.serializerByType(java.sql.Date.class, new JsonSerializer<java.sql.Date>() {
                @Override
                public void serialize(java.sql.Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeString(value != null ? new SimpleDateFormat(DATE_PATTERN).format(value) : null);
                }
            });

            // LocalDateTime -> yyyy-MM-dd HH:mm:ss
            builder.serializerByType(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
                @Override
                public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeString(value != null ? value.format(DATE_TIME_FORMATTER) : null);
                }
            });

            // LocalDate -> yyyy-MM-dd
            builder.serializerByType(LocalDate.class, new JsonSerializer<LocalDate>() {
                @Override
                public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeString(value != null ? value.format(DATE_FORMATTER) : null);
                }
            });
        };
    }

    // 注册各个 Repository Service
    @Bean
    public RepositoryService repositoryService(ProcessEngine processEngine) {
        return processEngine.getRepositoryService();
    }

    @Bean
    public RuntimeService runtimeService(ProcessEngine processEngine) {
        return processEngine.getRuntimeService();
    }

    @Bean
    public TaskService taskService(ProcessEngine processEngine) {
        return processEngine.getTaskService();
    }

    @Bean
    public HistoryService historyService(ProcessEngine processEngine) {
        return processEngine.getHistoryService();
    }

    @Bean
    public ManagementService managementService(ProcessEngine processEngine) {
        return processEngine.getManagementService();
    }

    @Bean
    public IdentityService identityService(ProcessEngine processEngine) {
        return processEngine.getIdentityService();
    }

    @Bean
    public FormService formService(ProcessEngine processEngine) {
        return processEngine.getFormService();
    }

    @Bean
    public DynamicBpmnService dynamicBpmnService(ProcessEngine processEngine) {
        return processEngine.getDynamicBpmnService();
    }

    @Bean
    @ConditionalOnMissingBean
    public WorkflowUserService workflowUserService() {
        return new DemoUserImpl();
    }

    @Bean
    @ConditionalOnMissingBean
    public WorkflowRoleService workflowRoleService() {
        return new DemoRoleImpl();
    }

    @Bean
    @ConditionalOnMissingBean
    public WorkflowDeptService workflowDeptService() {
        return new DemoDeptImpl();
    }

    @Bean
    @ConditionalOnMissingBean
    public WorkflowCategoryService workflowCategoryService() {
        return new DemoCategoryImpl();
    }

}